package storage.v1;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;

public abstract class AbstractStorageWithRegistry<T> {

	protected String prefix;

	protected abstract void writeToFile(File f, T content, String label);

	protected abstract T readFromFile(File f);

	protected File registerFile;
	private File rootDir;
	private File destDir;
	private HashMap<String,Integer> mapRef;
	private HashSet<Integer> indSetRef;
	boolean mapInited = false;
	StringBuilder wholeRegistryText = new StringBuilder();
	StringBuilder bld = null ;

	public AbstractStorageWithRegistry( String registerPath, String prefix ) {
		
		this.prefix = prefix ;
		bld = new StringBuilder( prefix + "_" ) ;
		registerFile = new File( registerPath + prefix.replace("/","").replace("\\", "") +"-reg.txt" ) ;
		String rootPath =null ;
		
		if( !registerFile.exists() ){
			try {
				registerFile.createNewFile() ;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		else{
			StringTokenizer tokenizer = readRegistryFile();
			if( tokenizer != null && tokenizer.hasMoreTokens() )
				rootPath = tokenizer.nextToken() ;
		}
		if( rootPath == null )
			rootPath = registerPath ;
		
		setRootPath(rootPath);
	}	

	public void setRootPath(String rootPath) {
		if( rootPath == null )
			return ;	
		
		rootDir = new File(rootPath) ;
		rootDir.mkdirs() ;
		if( !rootDir.canRead() )
		{
			rootDir = null ;
			return ;
		}
		initMap() ;
	}

	public String getRootpath() {
		if( rootDir == null )
			return null ;
		
		final String result = rootDir.getPath();
		if( result == null )
			return null ;
		
		return result ;
	}

	public T getContent(String label) {
		
		initMap() ;
		Integer index = mapRef.get(label) ;
		if( index == null )
			return null ;
		
		String fileName = createFileName(index);
		File f = new File(rootDir, fileName) ;
		if( !f.exists() )
			return null ;
		
		T result = readFromFile( f ) ;
		return result ;
	}

	private String createFileName(Integer index) {
		int l = bld.length() ;
		bld.append( index ) ;
		String result = bld.toString() ;
		bld.delete( l, bld.length() ) ;
		
		return result;
	}

	public boolean isEmpty() {
		initMap() ;
		if( mapRef == null )
			return false ;
		
		final int size = mapRef.size();
		return size != 0 ;
	}

	public void addContent(String label, T content) {
		initMap() ;
		if( mapRef==null )
			return ;
		
		Integer index = mapRef.get(label) ;
		
		//initSet() ;
		if( index == null )
		{
			for( index = mapRef.size() ; indSetRef.contains(index) ; index++ ) ;
			indSetRef.add(index) ;
			mapRef.put(label, index) ;
			wholeRegistryText.append(label) ;
			wholeRegistryText.append("\n") ;
			wholeRegistryText.append(index) ;
			wholeRegistryText.append("\n") ;
			saveRegistry() ;
		}
		
		String fileName = createFileName(index) ;
		writeToFile( new File( this.rootDir, fileName), content, label) ;
	}

	private void saveRegistry() {
		
		String content = wholeRegistryText.toString() ;
		try {
			DataOutputStream dos = new DataOutputStream( new FileOutputStream(this.registerFile )) ;
			byte[] byteContent = content.getBytes("UTF-8") ;
			dos.write( byteContent) ;
			dos.close() ;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	public void initMap() {		
		
		if( mapRef == null){
			mapRef = new HashMap<String,Integer>() ;
			mapInited = false ;
		}
		
		if( mapInited )
			return ;
		
		String path = null ;
		StringTokenizer tokenizer = readRegistryFile();
		
		if( tokenizer != null && tokenizer.hasMoreTokens() )
			path = tokenizer.nextToken() ;
			
		if( path == null ){
			if( this.rootDir != null )
				path = this.rootDir.getPath() ;
		}
		
		if( path == null )
			return ;
			
		wholeRegistryText.delete(0, wholeRegistryText.length()) ;
		wholeRegistryText.append(path) ;
		wholeRegistryText.append("\n") ;
		
		if( tokenizer != null )
		{
			String lableStr, indexStr;
			while(true){
				if(!tokenizer.hasMoreTokens() )
					break ;
				
				lableStr = tokenizer.nextToken() ;
				
				if(!tokenizer.hasMoreTokens() )
					break ;
	
				indexStr = tokenizer.nextToken() ;
	
				wholeRegistryText.append( lableStr ) ;
				wholeRegistryText.append("\n") ;
				wholeRegistryText.append( indexStr ) ;
				wholeRegistryText.append("\n") ;
				int index ;
				try{
					index = Integer.parseInt(indexStr) ;
				}
				
				catch( NumberFormatException e ){
					continue ;
				}
				
				mapRef.put( lableStr, index) ;
	
			}
		}
		initSet();
		mapInited = true ;		
	}

	protected StringTokenizer readRegistryFile() {
		StringTokenizer tokenizer = null ;
	
		String content = readUTFContent( registerFile );
		if( content != null )
			tokenizer = new StringTokenizer(content) ;
		
		return tokenizer;
	}

	protected String readUTFContent(File f) {
		ArrayList<byte[]> lst = new ArrayList<byte[]>() ;
		int size = 0 ;
		
		try {
			DataInputStream dis = new DataInputStream( new FileInputStream( f ) );		
			
			for( int s = dis.available() ; s > 0 ; s = dis.available() ){
				size += s ;
				byte[] bArr = new byte[s] ;
				dis.readFully(bArr) ;
				lst.add(bArr) ;
			}
			dis.close() ;			
		
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if( lst.size() == 0 )
			return null ;
		
		byte[] totalArr = new byte[size] ;
		int pos = 0 ;
		for( byte[] bArr : lst ){
			final int l = bArr.length;
			System.arraycopy( bArr, 0, totalArr, pos, l ) ;
			pos += l ;
		}
		
		String content = null;
		try {
			content = new String( totalArr, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return content;
	}

	private void initSet() {
		if( mapRef == null )
			return ;
		
		indSetRef = new HashSet<Integer>( mapRef.values() ) ;
	}

}